package quick.start.core.binding;

import java.util.ArrayList;
import java.util.Collection;

public class Observable<T> implements IObservable<T> {

    private WeakList<IObserver> observers = new WeakList<IObserver>();
    private T mValue;
    private final Class<T> mType;
    
    public Observable(Class<T> type){
    	mType = type;
    }
    
    protected void doSetValue(T newValue, Collection<Object> initators){
    	mValue = newValue;
    }
    
    protected final void setWithoutNotify(T newValue){
    	mValue = newValue;
    }
    
	@Override
	public Class<T> getType() {
		return mType;
	}

	@Override
	public final void subscribe(IObserver o) {
		observers.add(o);
	}

	@Override
	public void unsubscribe(IObserver o) {
		observers.remove(o);
	}

	@Override
	public IObserver[] getAllIObservers() {
		return observers.toArray(new IObserver[0]);
	}

	@Override
	public void notifyChanged(Object initiator) {
        ArrayList<Object> initiators = new ArrayList<Object>();
        initiators.add(initiator);
        this.notifyChanged(initiators);
	}

	@Override
	public void notifyChanged(Collection<Object> initiators) {
        initiators.add(this);
        for (Object o : observers.toArray()) {
            if (initiators.contains(o)) continue;
            ((IObserver) o).onPropertyChanged(this, initiators);
        }
	}

	@Override
	public void notifyChanged() {
        ArrayList<Object> initiators = new ArrayList<Object>();
        notifyChanged(initiators);
	}

	@Override
	public void set(T newValue, Collection<Object> initators) {
        if (initators.contains(this)) 
        	return;
        doSetValue(newValue, initators);
        initators.add(this);
        notifyChanged(initators);
	}

	@Override
	public void set(T newValue) {
        doSetValue(newValue, new ArrayList<Object>());
        notifyChanged(this);
	}

	@Override
	public void _setObject(Object newValue, Collection<Object> initators) {
        try {
            T value = this.getType().cast(newValue);
            if (value == null) return;
            this.set(value, initators);
        } catch (ClassCastException e) {
            return;
        }
	}

	@Override
	public T get() {
		return mValue;
	}

}
